package m3f.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Hashtable;

import m3f.matrix.SparseVector;

public class TripletsMatrixReader extends MatrixReader{
	
	private int columns;
	private int rows;
	private String filename;
	private BufferedReader reader;
	private Triplet currentTriplet;
	private SparseVector currentVector;
	private int startingRow;
	private int endingRow;
	
	public TripletsMatrixReader(){
		columns = 0;
		rows = 0;
	}	
	
	@Override
	public int getMatrixColumns(){
		return columns;
	}
	
	@Override
	public int getMatrixRows(){
		return rows;
	}
	
	@Override
	public void start(String filename){
		this.filename = filename;
		columns = 0;
		rows = 0;
		try{
			File source = new File(filename);
			FileInputStream stream = new FileInputStream(source);
			reader = new BufferedReader(new InputStreamReader(stream));
			String line;
			while((line = reader.readLine()) != null){
				Triplet t = new Triplet(line.split(" "));
				if(t.col >= columns){
					columns = t.col+1;
				}
				if(t.row >= rows){
					rows = t.row+1;
				}
			}
			reader.close();
			stream.close();
			reader = null;
		}catch(IOException e){
			System.err.println("Something went wrong opening " + filename);
			e.printStackTrace();
		}catch(NumberFormatException nfe){
			System.err.println("Corrupted file. Only numbers are allowed");
			nfe.printStackTrace();
		}
	}
	
	@Override
	public ArrayList<SparseVector> readVectors(int numVectors){
		ArrayList<SparseVector> results = new ArrayList<SparseVector>();
		Hashtable<Integer, SparseVector> vectors = new Hashtable<Integer, SparseVector>();
		int lastRow = -1;
		long start = System.currentTimeMillis();
		try{
			if(reader == null){
				File source = new File(filename);
				FileInputStream stream = new FileInputStream(source);
				reader = new BufferedReader(new InputStreamReader(stream));
				currentTriplet = null;
				currentVector = null;
				startingRow = 0;
				endingRow = 0;
			}
			if(currentVector != null && currentTriplet != null){
				startingRow = endingRow+1;
				vectors.put(startingRow, currentVector);
				endingRow = 0;
				lastRow = startingRow - 1;
			}
			int vectorsCounter = 0;
			String line;
			while((line = reader.readLine()) != null){
				if(currentTriplet != null){
					vectorsCounter += currentTriplet.row - lastRow;
					lastRow = currentTriplet.row;
					if(!vectors.containsKey(currentTriplet.row)){
						currentVector = new SparseVector();
						vectors.put(currentTriplet.row, currentVector);
					}
					currentVector.setQuick(currentTriplet.col, currentTriplet.value);
				}
				currentTriplet = new Triplet(line.split(" "));
				if(vectorsCounter > numVectors){
					vectors.remove(currentTriplet.row);
					break;
				}
				if(endingRow < currentTriplet.row) endingRow = currentTriplet.row-1;
			}
			// If the file has been completely read:
			if(line == null){
				currentVector.setQuick(currentTriplet.col, currentTriplet.value);
				endingRow++;
				reader.close();
				reader = null;
				currentTriplet = null;
				currentVector = null;
				//System.out.println(" Closing file");
			}
		}catch(IOException e){
			System.err.println("Something went wrong opening " + filename);
			e.printStackTrace();
		}catch(NumberFormatException nfe){
			System.err.println("Corrupted file. Only numbers are allowed");
			nfe.printStackTrace();
		}
		// Move vectors from the hash table to a list
		for(int i = startingRow; i <= endingRow; i++){
			SparseVector v = vectors.get(i);
			if(v != null){
				if(normalizing()) v.normalizeL1();
				results.add(v);
			} else {
				results.add(new SparseVector());
			}
		}
		//System.out.println(results.size() + " vectors read from " + filename + " ("+(System.currentTimeMillis()-start)+" ms)");
		return results;
	}
}

class Triplet{
	int row;
	int col;
	double value;
	
	public Triplet(String[] values) {
		row = Integer.parseInt(values[0]) - 1;
		col = Integer.parseInt(values[1]) - 1;
		value = Double.parseDouble(values[2]);
	}
	
	public String toString(){
		return row + "," + col + "," + value;
	}
}